package com.jht.bletool.characteristic.ftms;

import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;

import androidx.annotation.Keep;

import top.codestudy.annotation_uuid.MyUUID;

import com.hjq.toast.ToastUtils;
import com.jht.bletool.characteristic.TranslateData;
import com.jht.bletool.util.BaseUtils;
import com.jht.bletool.util.ComputerUtil;

import java.util.LinkedList;
import java.util.Queue;
//判断处理ok
@Keep
@MyUUID(uuid = "00002ace-0000-1000-8000-00805f9b34fb")
public class CrossTrainerData implements TranslateData {
    private final String TAG = "CrossTrainerData";
    private static CrossTrainerData data;
    private byte[] flags = new byte[3];

    private byte[] instantaneous_speed;
    private byte[] average_speed;
    private byte[] total_distance;
    private byte[] step_per_minute;
    private byte[] average_step_rate;
    private byte[] stride_count;
    private byte[] positive_elevation_gain;
    private byte[] negative_elevation_gain;
    private byte[] inclination;
    private byte[] ramp_angle_setting;
    private byte[] resistance_level;
    private byte[] instantaneous_power;
    private byte[] average_power;
    private byte[] total_energy;
    private byte[] energy_per_hour;
    private byte[] energy_per_minute;
    private byte[] heart_rate;
    private byte[] metabolic_equivalent;
    private byte[] elapsed_time;
    private byte[] remaining_time;

    private boolean InsSpeed = false;
    private boolean avgSpeed = false;
    private boolean totalDistance = false;
    private boolean StepCountPresent = false;
    private boolean strideCount = false;
    private boolean elevationGain = false;
    private boolean inclinationAndRampAngleSetting = false;
    private boolean resistanceLevel = false;
    private boolean insPower = false;
    private boolean avgPower = false;
    private boolean energy = false;
    private boolean heartRate = false;
    private boolean metabolic = false;
    private boolean elapsedTime = false;
    private boolean remainingTime = false;

    private boolean movementDirection = false; //Forward or Backward

    private boolean flagChecked = false;
    private int expect_data_length = 0;
    private byte[] valueData;

    public static CrossTrainerData getInstance()
    {
        data = new CrossTrainerData();
        return data;
    }

    public CrossTrainerData(){}
    private boolean isInvalidData = false;
    byte[] value1;
    public CrossTrainerData(BluetoothGattCharacteristic characteristic){
        byte[] value = characteristic.getValue();
        value1= value;
        valueData = value;
        parseData(value);
        for(int i=0;i<value.length;i++){
            Log.i(TAG, "CrossTrainerData: i = " + i +"; ox" + Integer.toHexString(BaseUtils.byte1ToInt(value[i])));
        }
    }

    public void parseData(byte[] buffer)
    {
        System.arraycopy(buffer, 0, flags, 0, 3);
        checkEachFieldIfSupported();
        parseEachSupportedFeature(buffer);
    }

    private void checkEachFieldIfSupported()
    {
        if(flagChecked)
        {
            return;
        }
        byte firstbyte = (byte) (flags[0] ^ 0xFD); // 0xFD means all features are supported in 1st byte;
        byte secondbyte = (byte) (flags[1] ^ 0xFF); // 0xFF mean all features are supported in 2nd byte;
        byte threebyte = (byte) (flags[2] ^ 0xFF);

        expect_data_length = 3;

        if(isBitZero(firstbyte, 0))
        {
            //instantaneous speed is supported
            InsSpeed = true;
            instantaneous_speed = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "instantaneous speed is supported");
        }else {
            Log.e(TAG,"数据过多！显示数据不可靠！");
        }
        if(isBitZero(firstbyte, 1))
        {
            //average speed is supported
            avgSpeed = true;
            average_speed = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "average speed is supported");
        }

        if(isBitZero(firstbyte, 2))
        {
            //totalDistance is supported
            totalDistance = true;
            total_distance = new byte[3];
            expect_data_length += 3;
            Log.d(TAG,"totalDistance is supported");
        }
        if(isBitZero(firstbyte, 3))
        {
            //Step Count present is supported
            StepCountPresent = true;
            step_per_minute = new byte[2];
            average_step_rate = new byte[2];
            expect_data_length += 4;
            Log.d(TAG,"Step Count present is supported");
        }
        if(isBitZero(firstbyte, 4))
        {
            //Stride Count present is supported
            strideCount = true;
            stride_count = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"Stride Count present is supported");
        }
        if(isBitZero(firstbyte, 5))
        {
            //elevationGain is supported
            elevationGain = true;
            positive_elevation_gain = new byte[2];
            negative_elevation_gain = new byte[2];
            expect_data_length += 4;
            Log.d(TAG,"elevationGain is supported");
        }
        if(isBitZero(firstbyte, 6))
        {
            //inclinationAndRampAngleSetting is supported
            inclinationAndRampAngleSetting = true;
            inclination = new byte[2];
            ramp_angle_setting = new byte[2];
            expect_data_length += 4;
            Log.d(TAG,"inclinationAndRampAngleSetting power is supported");
        }
        if(isBitZero(firstbyte, 7))
        {
            //resistance level is supported
            resistanceLevel = true;
            resistance_level = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"resistance level is supported");
        }
        if(isBitZero(secondbyte, 0))
        {
            // insPower features are supported
            insPower = true;
            instantaneous_power = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"insPower is supported");
        }
        if(isBitZero(secondbyte, 1))
        {
            //avgPower is supported
            avgPower = true;
            average_power = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"avgPower is supported");
        }
        if(isBitZero(secondbyte, 2))
        {
            //energy is supported
            energy = true;
            total_energy = new byte[2];
            energy_per_hour = new byte[2];
            energy_per_minute = new byte[1];
            expect_data_length += 5;
            Log.d(TAG,"energy is supported");
        }
        if(isBitZero(secondbyte, 3))
        {
            // heartRate are supported
            heartRate = true;
            heart_rate = new byte[1];
            expect_data_length += 1;
            Log.d(TAG,"heartRate is supported");
        }
        if(isBitZero(secondbyte, 4))
        {
            //metabolic is supported
            metabolic = true;
            metabolic_equivalent = new byte[1];
            expect_data_length += 1;
            Log.d(TAG,"metabolic is supported");
        }


        if(isBitZero(secondbyte, 5))
        {
            //elapsed time is supported
            elapsedTime = true;
            elapsed_time = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"elapsed time is supported");
        }
        if(isBitZero(secondbyte, 6))
        {
            //remaining time is supported
            remainingTime = true;
            remaining_time = new byte[2];
            expect_data_length += 2;
            Log.d(TAG,"remaining time is supported");
        }
        if(isBitZero(secondbyte, 7))
        {
            //movementDirection is supported
            movementDirection = true;
            Log.d(TAG,"movementDirection time is Backward ");
        }else {
            Log.d(TAG,"movementDirection time is Forward ");
        }
        flagChecked = true;
    }


    /**
     *
     * @param value, the xor result, 0 means feature is supported. 1 means feature is not supported
     * @param bit, the bit of value to check
     * @return
     */
    private boolean isBitZero(byte value, int bit)
    {
        Queue queue = new LinkedList();
        String s = "";

        int power2 = (int) Math.pow(2, bit);
        int tmp = value & power2;
        if(tmp == 0)
        {
            return true;
        }else{
            return false;
        }
    }

    private void parseEachSupportedFeature(byte[] buffer)
    {
        if(buffer.length < expect_data_length)
        {
            isInvalidData = true;
            Log.d(TAG, "buffer length is "+buffer.length);
            Log.d(TAG, "expect data length is "+expect_data_length);
            return;
        }

        int startIndex = 3;
        if(InsSpeed) {
            System.arraycopy(buffer, startIndex, instantaneous_speed, 0, 2);
            startIndex += 2;
        }
        if(avgSpeed) {
            System.arraycopy(buffer, startIndex, average_speed, 0, 2);
            startIndex += 2;
        }
        if(totalDistance) {
            System.arraycopy(buffer, startIndex, total_distance, 0, 3);
            startIndex += 3;
        }
        if(StepCountPresent) {
            System.arraycopy(buffer, startIndex, step_per_minute, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, average_step_rate, 0, 2);
            startIndex += 2;
        }
        if(strideCount) {
            System.arraycopy(buffer, startIndex, stride_count, 0, 2);
            startIndex += 2;
        }

        if(elevationGain) {
            System.arraycopy(buffer, startIndex, positive_elevation_gain, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, negative_elevation_gain, 0, 2);
            startIndex += 2;
        }

        if(inclinationAndRampAngleSetting) {
            System.arraycopy(buffer, startIndex, inclination, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, ramp_angle_setting, 0, 2);
            startIndex += 2;
        }

        if(resistanceLevel) {
            System.arraycopy(buffer, startIndex, resistance_level, 0, 2);
            startIndex += 2;
        }

        if(insPower) {
            System.arraycopy(buffer, startIndex, instantaneous_power, 0, 2);
            startIndex += 2;
        }
        if(avgPower) {
            System.arraycopy(buffer, startIndex, average_power, 0, 2);
            startIndex += 2;
        }
        if(energy) {
            System.arraycopy(buffer, startIndex, total_energy, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, energy_per_hour, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, energy_per_minute, 0, 1);
            startIndex += 1;
        }

        if(heartRate) {
            System.arraycopy(buffer, startIndex, heart_rate, 0, 1);
            startIndex += 1;
        }

        if(metabolic) {
            System.arraycopy(buffer, startIndex, metabolic_equivalent, 0, 1);
            startIndex += 1;
        }
        if(elapsedTime) {
            System.arraycopy(buffer, startIndex, elapsed_time, 0, 2);
            startIndex += 2;
        }
        if(remainingTime) {
            System.arraycopy(buffer, startIndex, remaining_time, 0, 2);
        }
    }

    public double getInstantaneousSpeed()
    {
        if(InsSpeed) {
            return ComputerUtil.multiply(BaseUtils.bytes2ToInt(instantaneous_speed, 0),"0.01");
        }else{
            return -9999;
        }
    }

    public double getAverageSpeed()
    {
        if(avgSpeed)
        {
            return ComputerUtil.multiply(BaseUtils.bytes2ToInt(average_speed, 0),"0.01");
        }else{
            return -9999;
        }
    }

    public int getTotalDistance()
    {
        if(totalDistance)
        {
            return BaseUtils.byte3ToInt(total_distance, 0);
        }else{
            return -9999;
        }
    }
    public int getStepPerMinute(){

        if(StepCountPresent)
        {
            return BaseUtils.bytes2ToInt(step_per_minute, 0);
        }else{
            return -9999;
        }
    }

    public int getAverageStepRate(){

        if(StepCountPresent)
        {
            return BaseUtils.bytes2ToInt(average_step_rate, 0);
        }else{
            return -9999;
        }
    }

    public double getStrideCount(){
        if(strideCount)
        {
            return ComputerUtil.multiply(BaseUtils.bytes2ToInt(stride_count, 0),"0.1");
        }else{
            return -9999;
        }
    }

    public int getPositiveElevationGain(){
        if(elevationGain)
        {
            return BaseUtils.bytes2ToInt(positive_elevation_gain, 0);
        }else{
            return -9999;
        }
    }
    public int getNegativeElevationGain(){
        if(elevationGain)
        {
            return BaseUtils.bytes2ToInt(negative_elevation_gain, 0);
        }else{
            return -9999;
        }
    }

    public double getInclination()
    {
        if(inclinationAndRampAngleSetting)
        {
            return ComputerUtil.multiply(BaseUtils.bytes2ToInt(inclination, 0),"0.1");
        }else{
            return -9999;
        }
    }
    public double getRampAngleSetting()
    {
        if(inclinationAndRampAngleSetting)
        {
            return ComputerUtil.multiply(BaseUtils.bytes2ToInt(ramp_angle_setting, 0),"0.1");
        }else{
            return -9999;
        }
    }

    public double getResistanceLevel()
    {
        if(resistanceLevel)
        {
            return ComputerUtil.multiply(BaseUtils.bytes2ToInt(resistance_level, 0),"0.1");
        }else{
            return -9999;
        }
    }

    public int getInstantaneousPower()
    {
        if(insPower)
        {
            return BaseUtils.bytes2ToInt(instantaneous_power, 0);
        }else{
            return -9999;
        }
    }

    public int getAveragePower()
    {
        if(avgPower)
        {
            return BaseUtils.bytes2ToInt(average_power, 0);
        }else{
            return -9999;
        }
    }

    public int getTotalEnergy()
    {
        if(energy)
        {
            return BaseUtils.bytes2ToInt(total_energy, 0);
        }else{
            return -9999;
        }
    }

    public int getEnergyPerHour()
    {
        if(energy)
        {
            return BaseUtils.bytes2ToInt(energy_per_hour, 0);
        }else{
            return -9999;
        }
    }

    public int getEnergyPerMinute()
    {
        if(energy)
        {
            return BaseUtils.byte1ToInt(energy_per_minute[0]);
        }else{
            return -9999;
        }
    }

    public int getHeartRate()
    {
        if(heartRate)
        {
            return BaseUtils.byte1ToInt(heart_rate[0]);
        }else{
            return -9999;
        }
    }

    public double getMetabolicEquivalent()
    {
        if(metabolic)
        {
            return ComputerUtil.multiply(BaseUtils.byte1ToInt(metabolic_equivalent[0]),"0.1");
        }else{
            return -9999;
        }
    }

    public int getElapedTime()
    {
        if(elapsedTime)
        {
            return BaseUtils.bytes2ToInt(elapsed_time, 0);
        }else{
            return -9999;
        }
    }

    public int getRemainingTime()
    {
        if(remainingTime)
        {
            return BaseUtils.bytes2ToInt(remaining_time, 0);
        }else{
            return -9999;
        }
    }

    public String getMovementDirection(){
        if (movementDirection){
            return "Backward";
        }else {
            return "Forward";
        }
    }

    public String convert2String(){
        return    "instant speed :  " + getInstantaneousSpeed()
                + "   km/h ;\n average speed :  " + getAverageSpeed()
                + "   km/h ;\n total distance :  " + getTotalDistance()
                + "   m ;\n TotalEnergy :  " + getTotalEnergy()
                + "   Calorie ;\n Instantaneous Power : " + getInstantaneousPower()
                + "   watt ;\n Average Power : " + getAveragePower()
                + "   watt ;\n inclination :  " + getInclination()
                + "   percentage ;\n HeartRate :  " + getHeartRate()
                + "   bpm ;\n elapsed time :  " + getElapedTime()
                + "   second ;\n MovementDirection :   " + getMovementDirection()
                + "  ;\n StepPerMinute :  " + getStepPerMinute()
                + "  ;\n StrideCount :  " + getStrideCount()
                + "  ;\n AverageStepRate :  "+ getAverageStepRate()
                + "  ;\n ResistanceLevel :  "+ getResistanceLevel()
                +"   ;\n Energy Per Hour : " + getEnergyPerHour()
                +"  calorie;\n Energy Per Minute : " + getEnergyPerMinute()
                +"  calorie;\n RemainingTime :  " + getRemainingTime()
                +"  second;\n Positive Elevation Gain : " + getPositiveElevationGain()
                +"  metre;\n Negative Elevation Gain : " + getNegativeElevationGain()
                +"  metre;\n Ramp Angle Setting : " + getRampAngleSetting()
                +"  Degree;\n Metabolic Equivalent: " + getMetabolicEquivalent()
                +"\n "
                ;
    }

    @Override
    public String merge(TranslateData translateData) {
        CrossTrainerData temp = (CrossTrainerData)translateData;
        if (temp.InsSpeed){
            this.instantaneous_speed = temp.instantaneous_speed;
            this.InsSpeed=true;
        }
        if (temp.avgSpeed){
            this.average_speed = temp.average_speed;
            this.avgSpeed=true;
        }
        if(temp.totalDistance) {
            this.total_distance = temp.total_distance;
            this.totalDistance=true;
        }
        if(temp.StepCountPresent) {
            this.step_per_minute = temp.step_per_minute;
            this.average_step_rate = temp.average_step_rate;
            this.StepCountPresent=true;
        }
        if(temp.strideCount) {
            this.stride_count= temp.stride_count;
            this.strideCount=true;
        }
        if(temp.elevationGain) {
            this.positive_elevation_gain= temp.positive_elevation_gain;

            this.negative_elevation_gain= temp.negative_elevation_gain;
            this.elevationGain=true;
        }

        if(temp.inclinationAndRampAngleSetting) {
            this.inclination= temp.inclination;

            this.ramp_angle_setting= temp.ramp_angle_setting;
            this.inclinationAndRampAngleSetting=true;
        }

        if(temp.resistanceLevel) {
            this.resistance_level= temp.resistance_level;
            this.resistanceLevel=true;
        }

        if(temp.insPower) {
            this.instantaneous_power= temp.instantaneous_power;
            this.insPower=true;
        }
        if(temp.avgPower) {
            this.average_power= temp.average_power;
            this.avgPower=true;
        }
        if(temp.energy) {
            this.total_energy= temp.total_energy;

            this.energy_per_hour= temp.energy_per_hour;

            this.energy_per_minute= temp.energy_per_minute;
            this.energy=true;
        }

        if(temp.heartRate) {
            this.heart_rate= temp.heart_rate;
            this.heartRate=true;
        }

        if(temp.metabolic) {
            this.metabolic_equivalent= temp.metabolic_equivalent;
            this.metabolic=true;
        }
        if(temp.elapsedTime) {
            this.elapsed_time= temp.elapsed_time;
            this.elapsedTime=true;
        }
        if(temp.remainingTime) {
            this.remaining_time= temp.remaining_time;
            this.remainingTime=true;
        }

        return null;
    }

    @Override
    public boolean hasMoreData() {
        return !InsSpeed;
    }

    @Override
    public byte[] getData() {
        return valueData;
    }

    @Override
    public boolean isInvalidData() {
        return isInvalidData;
    }
}
